<?php

namespace Core;

use DI\ContainerBuilder;
use Doctrine\Common\Annotations\AnnotationReader;
use Doctrine\Common\Annotations\AnnotationRegistry;

class BeanFactory
{
    private static $env = []; //env 配置文件
    private static $cotainer; //ioc 容器
    private static $handlers = [];

    /**
     * 初始化函数，使用static静态的方式，将不需要实例化，可以使用class::function直接调用
     * @throws \Exception
     */
    public static function init() //初始化函数
    {
        self::$env = parse_ini_file(ROOT_PAHT . "/env");

        $builder = new ContainerBuilder(); //初始化容器Builder
        $builder->useAnnotations(true); //启用注解，主要是用它的Inject注解
        self::$cotainer = $builder->build(); //容器初始化

        $handlers = glob(ROOT_PAHT . "/core/annotationshandlers/*.php");
        foreach ($handlers as $handler){
            $handler = require_once $handler;
            self::$handlers = array_merge(self::$handlers,$handler);
        }
        //self::$handlers = require_once(ROOT_PAHT . "/core/annotations/AnnotationHandlers.php");
        //设置注解加载
        $loader = require __DIR__.'/../vendor/autoload.php';
        AnnotationRegistry::registerLoader([$loader,'loadClass']);


        //扫描配置项，并把所有的配置文件中要扫描的类加载一遍
        $scans = [
            ROOT_PAHT . "/core/init" => "Core\\",   //先扫描框架内部必须要扫描的文件夹
            self::getEnv("scan_dir", ROOT_PAHT . "/app") => self::getEnv("scan_root_namespace", "App\\")
        ];
        foreach ($scans as $scan_dir => $namespace) {
            self::ScanBeans($scan_dir, $namespace); //扫描
        }
    }

    public static function ScanBeans($scan_dir,$scan_root_namespace)
    {
        $files = glob($scan_dir . "/*.php");    //在文件夹下查询所有后缀是 .php的文件，并返回数组到变量$files
        foreach ($files as $file) {
            require_once $file;
        }
        //初始化AnnotationReader，并注册自动加载的注解类位于 Core\annotations目录下，这个目录下有几个注解类，这里就加载几个注解类
        $reader = new AnnotationReader();
        //获取已经读到的所有需要被注解的类
        foreach (get_declared_classes() as $class) {
            //判断只需要配置文件夹下面的类
            if (strstr($class, $scan_root_namespace)) {
                $ref_class = new \ReflectionClass($class);//目标类的反射对象
                $class_annos = $reader->getClassAnnotations($ref_class);//获取所有类注解

                /////下方是处理 类注解
                foreach ($class_annos as $class_anno) {
                    $handler = self::$handlers[get_class($class_anno)]; //获取handler处理过程
                    $instance = self::$cotainer->get($ref_class->getName());
                    //处理属性注解
                    self::handlerPropAnno($instance, $ref_class, $reader);
                    //处理方法注解
                    self::handlerMethodAnno($instance, $ref_class, $reader);
                    ///
                    $handler($instance, self::$cotainer, $class_anno); //执行类注解处理
                }
            }
        }
    }

    /**
     * 获取env文件中的配置内容
     * @param string $key
     * @param string $default
     * @return mixed|string
     */
    private static function getEnv(string $key, string $default = "")
    {
        if (isset(self::$env[$key])) return self::$env[$key];
        return $default;
    }

    /**
     * 处理属性注解
     * @param $instance
     * @param \ReflectionClass $ref_class
     * @param AnnotationReader $reader
     */
    private static function handlerPropAnno(&$instance, \ReflectionClass $ref_class, AnnotationReader $reader)
    {
        $props = $ref_class->getProperties();//取出反射对象所有属性
        foreach ($props as $prop) {
            $prop_annos = $reader->getPropertyAnnotations($prop);
            foreach ($prop_annos as $prop_anno) {
                $handler = self::$handlers[get_class($prop_anno)];
                $instance = $handler($prop, $instance, $prop_anno);//处理属性注解
            }
        }
    }

    /**
     * 处理方法注解
     * @param $instance
     * @param \ReflectionClass $ref_class
     * @param AnnotationReader $reader
     */
    private static function handlerMethodAnno(&$instance, \ReflectionClass $ref_class, AnnotationReader $reader)
    {
        $methods = $ref_class->getMethods();//取出反射对象所有方法
        foreach ($methods as $method) {
            $method_annos = $reader->getMethodAnnotations($method);
            foreach ($method_annos as $method_anno) {
                $handler = self::$handlers[get_class($method_anno)];
                $instance = $handler($method, $instance, $method_anno);//处理方法注解
            }
        }
    }

    public static function getBean($name)
    {
        return self::$cotainer->get($name);
    }

}
